1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use clap::ValueEnum;
9use clap::builder::PossibleValue;
10use msg_tool_macro::*;
11use overf::wrapping;
12use std::io::{Read, Seek, Write};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum CircusCrxMode {
17 Fixed(u8),
19 Auto,
21 Origin,
23 Best,
25}
26
27impl Default for CircusCrxMode {
28 fn default() -> Self {
29 CircusCrxMode::Auto
30 }
31}
32
33impl CircusCrxMode {
34 pub fn for_importing(&self) -> Self {
36 match self {
37 CircusCrxMode::Auto => CircusCrxMode::Origin,
38 _ => *self,
39 }
40 }
41
42 pub fn for_creating(&self) -> Self {
44 match self {
45 CircusCrxMode::Auto => CircusCrxMode::Best,
46 CircusCrxMode::Origin => CircusCrxMode::Best,
47 _ => *self,
48 }
49 }
50
51 pub fn is_best(&self) -> bool {
53 matches!(self, CircusCrxMode::Best)
54 }
55
56 pub fn is_origin(&self) -> bool {
58 matches!(self, CircusCrxMode::Origin)
59 }
60}
61
62impl ValueEnum for CircusCrxMode {
63 fn value_variants<'a>() -> &'a [Self] {
64 &[
65 CircusCrxMode::Fixed(0),
66 CircusCrxMode::Fixed(1),
67 CircusCrxMode::Fixed(2),
68 CircusCrxMode::Fixed(3),
69 CircusCrxMode::Fixed(4),
70 CircusCrxMode::Auto,
71 CircusCrxMode::Origin,
72 CircusCrxMode::Best,
73 ]
74 }
75
76 fn to_possible_value(&self) -> Option<PossibleValue> {
77 Some(match self {
78 CircusCrxMode::Fixed(0) => PossibleValue::new("0").help("Row type 0"),
79 CircusCrxMode::Fixed(1) => PossibleValue::new("1").help("Row type 1"),
80 CircusCrxMode::Fixed(2) => PossibleValue::new("2").help("Row type 2"),
81 CircusCrxMode::Fixed(3) => PossibleValue::new("3").help("Row type 3"),
82 CircusCrxMode::Fixed(4) => PossibleValue::new("4").help("Row type 4"),
83 CircusCrxMode::Auto => PossibleValue::new("auto")
84 .help("When importing, use origin mode, otherwise use best mode."),
85 CircusCrxMode::Origin => PossibleValue::new("origin")
86 .help("Use origin mode for importing. When creating, fallback to best mode."),
87 CircusCrxMode::Best => PossibleValue::new("best").help("Try to use the best mode."),
88 _ => return None,
89 })
90 }
91}
92
93#[derive(Debug)]
94pub struct CrxImageBuilder {}
96
97impl CrxImageBuilder {
98 pub const fn new() -> Self {
100 CrxImageBuilder {}
101 }
102}
103
104impl ScriptBuilder for CrxImageBuilder {
105 fn default_encoding(&self) -> Encoding {
106 Encoding::Cp932
107 }
108
109 fn build_script(
110 &self,
111 data: Vec<u8>,
112 _filename: &str,
113 _encoding: Encoding,
114 _archive_encoding: Encoding,
115 config: &ExtraConfig,
116 _archive: Option<&Box<dyn Script>>,
117 ) -> Result<Box<dyn Script>> {
118 Ok(Box::new(CrxImage::new(MemReader::new(data), config)?))
119 }
120
121 fn extensions(&self) -> &'static [&'static str] {
122 &["crx"]
123 }
124
125 fn script_type(&self) -> &'static ScriptType {
126 &ScriptType::CircusCrx
127 }
128
129 fn is_image(&self) -> bool {
130 true
131 }
132
133 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
134 if buf_len >= 4 && buf.starts_with(b"CRXG") {
135 return Some(255);
136 }
137 None
138 }
139
140 fn can_create_image_file(&self) -> bool {
141 true
142 }
143
144 fn create_image_file<'a>(
145 &'a self,
146 data: ImageData,
147 writer: Box<dyn WriteSeek + 'a>,
148 options: &ExtraConfig,
149 ) -> Result<()> {
150 CrxImage::create_image(data, writer, options)
151 }
152}
153
154#[derive(Clone, Debug, StructPack, StructUnpack)]
155struct Clip {
156 field_0: u32,
157 img_width: u16,
158 img_height: u16,
159 clip_offset_x: u16,
160 clip_offset_y: u16,
161 clip_width: u16,
162 clip_height: u16,
163}
164
165#[derive(Clone, Debug, StructPack, StructUnpack)]
166struct Header {
167 inner_x: u16,
168 inner_y: u16,
169 width: u16,
170 height: u16,
171 version: u16,
172 flags: u16,
173 bpp: u16,
174 mode: u16,
175 #[skip_pack_if(self.version != 3)]
176 #[skip_unpack_if(version != 3)]
177 #[pvec(u32)]
178 clips: Vec<Clip>,
179}
180
181pub struct CrxImage {
183 header: Header,
184 color_type: ImageColorType,
185 data: Vec<u8>,
186 compress_level: u32,
187 keep_original_bpp: bool,
188 zstd: bool,
189 zstd_compression_level: i32,
190 row_type: CircusCrxMode,
191 canvas: bool,
192}
193
194impl std::fmt::Debug for CrxImage {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 f.debug_struct("CrxImage")
197 .field("header", &self.header)
198 .field("color_type", &self.color_type)
199 .field("data_length", &self.data.len())
200 .finish()
201 }
202}
203
204impl CrxImage {
205 pub fn new<T: Read + Seek>(data: T, config: &ExtraConfig) -> Result<Self> {
210 let mut reader = data;
211 let mut magic = [0; 4];
212 reader.read_exact(&mut magic)?;
213 if magic != *b"CRXG" {
214 return Err(anyhow::anyhow!("Invalid CRX image magic"));
215 }
216 let header: Header = reader.read_struct(false, Encoding::Utf8)?;
217 if header.version < 2 || header.version > 3 {
218 return Err(anyhow::anyhow!(
219 "Unsupported CRX version: {}",
220 header.version
221 ));
222 }
223 let color_type = if header.bpp == 0 {
224 ImageColorType::Bgr
225 } else if header.bpp == 1 {
226 ImageColorType::Bgra
227 } else {
228 return Err(anyhow::anyhow!("Unsupported CRX bpp: {}", header.bpp));
229 };
230 let compressed_size = if (header.flags & 0x10) == 0 {
231 let len = reader.stream_length()?;
232 (len - reader.stream_position()?) as u32
233 } else {
234 reader.read_u32()?
235 };
236 let compressed_data = reader.read_exact_vec(compressed_size as usize)?;
237 let uncompessed = if compressed_data.starts_with(&[0x28, 0xb5, 0x2f, 0xfd]) {
238 let mut decoder = zstd::Decoder::new(MemReaderRef::new(&compressed_data))?;
239 let mut decompressed_data = Vec::new();
240 decoder.read_to_end(&mut decompressed_data)?;
241 decompressed_data
242 } else {
243 let mut decompressed_data = Vec::new();
244 flate2::read::ZlibDecoder::new(MemReaderRef::new(&compressed_data))
245 .read_to_end(&mut decompressed_data)?;
246 decompressed_data
247 };
248 Ok(CrxImage {
249 header,
250 color_type,
251 data: uncompessed,
252 compress_level: config.zlib_compression_level,
253 keep_original_bpp: config.circus_crx_keep_original_bpp,
254 zstd: config.circus_crx_zstd,
255 zstd_compression_level: config.zstd_compression_level,
256 row_type: config.circus_crx_mode.for_importing(),
257 canvas: config.circus_crx_canvas,
258 })
259 }
260
261 pub fn with_canvas(mut self, canvas: bool) -> Self {
263 self.canvas = canvas;
264 self
265 }
266
267 pub fn draw_diff(&self, diff: &Self) -> Result<ImageData> {
271 let base_header = &self.header;
272 let diff_header = &diff.header;
273 let (img_width, img_height) =
274 if base_header.clips.is_empty() && diff_header.clips.is_empty() {
275 (
276 (base_header.width + base_header.inner_x)
277 .max(diff_header.width + diff_header.inner_x),
278 (base_header.height + base_header.inner_y)
279 .max(diff_header.height + diff_header.inner_y),
280 )
281 } else {
282 if base_header.clips.is_empty() {
283 let clip = &diff_header.clips[0];
284 (clip.img_width, clip.img_height)
285 } else {
286 let clip = &base_header.clips[0];
287 (clip.img_width, clip.img_height)
288 }
289 };
290 let base = self.export_image()?;
291 let mut nw = draw_on_canvas(
292 base,
293 img_width as u32,
294 img_height as u32,
295 base_header.inner_x as u32,
296 base_header.inner_y as u32,
297 )?;
298 draw_on_img(
299 &mut nw,
300 &diff.export_image()?,
301 diff_header.inner_x as u32,
302 diff_header.inner_y as u32,
303 )?;
304 Ok(nw)
305 }
306
307 fn decode_row0(
308 dst: &mut Vec<u8>,
309 mut dst_p: usize,
310 src: &[u8],
311 mut src_p: usize,
312 width: u16,
313 pixel_size: u8,
314 ) -> Result<usize> {
315 let mut prev_p = dst_p;
316 for _ in 0..pixel_size {
317 dst[dst_p] = src[src_p];
318 dst_p += 1;
319 src_p += 1;
320 }
321 let remaining = width - 1;
322 for _ in 0..remaining {
323 for _ in 0..pixel_size {
324 dst[dst_p] = src[src_p].overflowing_add(dst[prev_p]).0;
325 dst_p += 1;
326 src_p += 1;
327 prev_p += 1;
328 }
329 }
330 Ok(src_p)
331 }
332
333 fn decode_row1(
334 dst: &mut Vec<u8>,
335 mut dst_p: usize,
336 src: &[u8],
337 mut src_p: usize,
338 width: u16,
339 pixel_size: u8,
340 mut prev_row_p: usize,
341 ) -> Result<usize> {
342 for _ in 0..width {
343 for _ in 0..pixel_size {
344 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
345 dst_p += 1;
346 src_p += 1;
347 prev_row_p += 1;
348 }
349 }
350 Ok(src_p)
351 }
352
353 fn decode_row2(
354 dst: &mut Vec<u8>,
355 mut dst_p: usize,
356 src: &[u8],
357 mut src_p: usize,
358 width: u16,
359 pixel_size: u8,
360 mut prev_row_p: usize,
361 ) -> Result<usize> {
362 for _ in 0..pixel_size {
363 dst[dst_p] = src[src_p];
364 dst_p += 1;
365 src_p += 1;
366 }
367 let remaining = width - 1;
368 for _ in 0..remaining {
369 for _ in 0..pixel_size {
370 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
371 dst_p += 1;
372 src_p += 1;
373 prev_row_p += 1;
374 }
375 }
376 Ok(src_p)
377 }
378
379 fn decode_row3(
380 dst: &mut Vec<u8>,
381 mut dst_p: usize,
382 src: &[u8],
383 mut src_p: usize,
384 width: u16,
385 pixel_size: u8,
386 mut prev_row_p: usize,
387 ) -> Result<usize> {
388 let count = width - 1;
389 prev_row_p += pixel_size as usize;
390 for _ in 0..count {
391 for _ in 0..pixel_size {
392 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
393 dst_p += 1;
394 src_p += 1;
395 prev_row_p += 1;
396 }
397 }
398 for _ in 0..pixel_size {
399 dst[dst_p] = src[src_p];
400 dst_p += 1;
401 src_p += 1;
402 }
403 Ok(src_p)
404 }
405
406 fn decode_row4(
407 dst: &mut Vec<u8>,
408 dst_p: usize,
409 src: &[u8],
410 mut src_p: usize,
411 width: u16,
412 pixel_size: u8,
413 ) -> Result<usize> {
414 for offset in 0..pixel_size {
415 let mut dst_c = dst_p + offset as usize;
416 let mut remaining = width;
417 let value = src[src_p];
418 src_p += 1;
419 dst[dst_c] = value;
420 dst_c += pixel_size as usize;
421 remaining -= 1;
422 if remaining == 0 {
423 continue;
424 }
425 if value == src[src_p] {
426 src_p += 1;
427 let count = src[src_p] as u16;
428 src_p += 1;
429 remaining -= count;
430 for _ in 0..count {
431 dst[dst_c] = value;
432 dst_c += pixel_size as usize;
433 }
434 }
435 while remaining > 0 {
436 let value = src[src_p];
437 src_p += 1;
438 dst[dst_c] = value;
439 dst_c += pixel_size as usize;
440 remaining -= 1;
441 if remaining == 0 {
442 break;
443 }
444 if value == src[src_p] {
445 src_p += 1;
446 let count = src[src_p] as u16;
447 src_p += 1;
448 remaining -= count;
449 for _ in 0..count {
450 dst[dst_c] = value;
451 dst_c += pixel_size as usize;
452 }
453 }
454 }
455 }
456 Ok(src_p)
457 }
458
459 fn decode_image(
460 dst: &mut Vec<u8>,
461 src: &[u8],
462 width: u16,
463 height: u16,
464 pixel_size: u8,
465 encode_type: &mut Vec<u8>,
466 ) -> Result<()> {
467 let mut src_p = 0;
468 let mut dst_p = 0;
469 let mut prev_row_p = 0;
470 for _ in 0..height {
471 let data = src[src_p];
472 encode_type.push(data);
473 src_p += 1;
474 match data {
475 0 => {
476 src_p = Self::decode_row0(dst, dst_p, src, src_p, width, pixel_size)?;
477 }
478 1 => {
479 src_p =
480 Self::decode_row1(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
481 }
482 2 => {
483 src_p =
484 Self::decode_row2(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
485 }
486 3 => {
487 src_p =
488 Self::decode_row3(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
489 }
490 4 => {
491 src_p = Self::decode_row4(dst, dst_p, src, src_p, width, pixel_size)?;
492 }
493 _ => {
494 return Err(anyhow::anyhow!("Invalid row type: {}", data));
495 }
496 }
497 prev_row_p = dst_p;
498 dst_p += pixel_size as usize * width as usize;
499 }
500 Ok(())
501 }
502
503 fn encode_row0(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
504 let pixel_size = pixel_size as usize;
505 let mut src_p = y as usize * width as usize * pixel_size;
506 for _ in 0..pixel_size {
507 dst.push(src[src_p]);
508 src_p += 1;
509 }
510 for _ in 1..width {
511 for _ in 0..pixel_size {
512 dst.push(src[src_p].wrapping_sub(src[src_p - pixel_size]));
513 src_p += 1;
514 }
515 }
516 }
517
518 fn encode_row1(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
519 let pixel_size = pixel_size as usize;
520 let mut src_p = y as usize * width as usize * pixel_size;
521 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
522 for _ in 0..width {
523 for _ in 0..pixel_size {
524 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
525 src_p += 1;
526 prev_row_p += 1;
527 }
528 }
529 }
530
531 fn encode_row2(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
532 let pixel_size = pixel_size as usize;
533 let mut src_p = y as usize * width as usize * pixel_size;
534 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
535 for _ in 0..pixel_size {
536 dst.push(src[src_p]);
537 src_p += 1;
538 }
539 for _ in 1..width {
540 for _ in 0..pixel_size {
541 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
542 src_p += 1;
543 prev_row_p += 1;
544 }
545 }
546 }
547
548 fn encode_row3(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
549 let pixel_size = pixel_size as usize;
550 let mut src_p = y as usize * width as usize * pixel_size;
551 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size + pixel_size;
552 for _ in 0..width - 1 {
553 for _ in 0..pixel_size {
554 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
555 src_p += 1;
556 prev_row_p += 1;
557 }
558 }
559 for _ in 0..pixel_size {
560 dst.push(src[src_p]);
561 src_p += 1;
562 }
563 }
564
565 fn encode_row4(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
566 let pixel_size = pixel_size as usize;
567 let src_p = y as usize * width as usize * pixel_size;
568 for offset in 0..pixel_size {
569 let mut src_c = src_p + offset;
570 let mut remaining = width;
571 let value = src[src_c];
572 src_c += pixel_size;
573 dst.push(value);
574 remaining -= 1;
575 if remaining == 0 {
576 continue;
577 }
578 let mut count = 0;
579 loop {
580 if count as u16 >= remaining || count >= 255 || src[src_c] != value {
581 break;
582 }
583 src_c += pixel_size;
584 count += 1;
585 }
586 if count > 0 {
587 dst.push(value);
588 dst.push(count);
589 remaining -= count as u16;
590 }
591 while remaining > 0 {
592 let value = src[src_c];
593 src_c += pixel_size;
594 dst.push(value);
595 remaining -= 1;
596 if remaining == 0 {
597 break;
598 }
599 let mut count = 0;
600 loop {
601 if count as u16 >= remaining || count >= 255 || src[src_c] != value {
602 break;
603 }
604 src_c += pixel_size;
605 count += 1;
606 }
607 if count > 0 {
608 dst.push(value);
609 dst.push(count);
610 remaining -= count as u16;
611 }
612 }
613 }
614 }
615
616 fn encode_row_best(
617 dst: &mut Vec<u8>,
618 src: &[u8],
619 width: u16,
620 pixel_size: u8,
621 y: u16,
622 ) -> Result<()> {
623 let mut buf = Vec::with_capacity(width as usize * pixel_size as usize);
624 Self::encode_row0(&mut buf, src, width, pixel_size, y);
625 let mut compressed_len = {
626 let mut encoder =
627 flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
628 encoder.write_all(&buf)?;
629 let compressed_data = encoder.finish()?;
630 compressed_data.into_inner().len()
631 };
632 let mut buf_row_type = 0;
633 for row_type in 1..5u8 {
634 if y == 0 && row_type < 4 {
635 continue;
636 }
637 let mut newbuf = Vec::with_capacity(width as usize * pixel_size as usize);
638 match row_type {
639 1 => Self::encode_row1(&mut newbuf, src, width, pixel_size, y),
640 2 => Self::encode_row2(&mut newbuf, src, width, pixel_size, y),
641 3 => Self::encode_row3(&mut newbuf, src, width, pixel_size, y),
642 4 => Self::encode_row4(&mut newbuf, src, width, pixel_size, y),
643 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
644 };
645 let new_compressed_len = {
646 let mut encoder =
647 flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
648 encoder.write_all(&newbuf)?;
649 let compressed_data = encoder.finish()?;
650 compressed_data.into_inner().len()
651 };
652 if new_compressed_len < compressed_len {
653 compressed_len = new_compressed_len;
654 buf = newbuf;
655 buf_row_type = row_type;
656 }
657 }
658 dst.push(buf_row_type);
659 dst.extend_from_slice(&buf);
660 Ok(())
661 }
662
663 fn encode_image_best(src: &[u8], width: u16, height: u16, pixel_size: u8) -> Result<Vec<u8>> {
664 let size = width as usize * height as usize * pixel_size as usize + height as usize;
665 let mut dst = Vec::with_capacity(size);
666 for y in 0..height {
667 Self::encode_row_best(&mut dst, src, width, pixel_size, y)?;
668 }
669 Ok(dst)
670 }
671
672 fn encode_image_fixed(
673 src: &[u8],
674 width: u16,
675 height: u16,
676 pixel_size: u8,
677 row_type: u8,
678 ) -> Result<Vec<u8>> {
679 let size = width as usize * height as usize * pixel_size as usize + height as usize;
680 let mut dst = Vec::with_capacity(size);
681 for y in 0..height {
682 let row_type = if y == 0 && row_type != 0 && row_type != 4 {
683 0
684 } else {
685 row_type
686 };
687 dst.push(row_type);
688 match row_type {
689 0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
690 1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
691 2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
692 3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
693 4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
694 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
695 };
696 }
697 Ok(dst)
698 }
699
700 fn encode_image_origin(
701 src: &[u8],
702 width: u16,
703 height: u16,
704 pixel_size: u8,
705 row_type: &[u8],
706 ) -> Result<Vec<u8>> {
707 if row_type.len() != height as usize {
708 return Err(anyhow::anyhow!("Row type length does not match height"));
709 }
710 let size = width as usize * height as usize * pixel_size as usize + height as usize;
711 let mut dst = Vec::with_capacity(size);
712 for y in 0..height {
713 let row_type = row_type[y as usize];
714 dst.push(row_type);
715 match row_type {
716 0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
717 1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
718 2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
719 3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
720 4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
721 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
722 };
723 }
724 Ok(dst)
725 }
726
727 pub fn create_image<T: Write + Seek>(
733 mut data: ImageData,
734 mut writer: T,
735 config: &ExtraConfig,
736 ) -> Result<()> {
737 let header = Header {
738 inner_x: 0,
739 inner_y: 0,
740 width: data.width as u16,
741 height: data.height as u16,
742 version: 2,
743 flags: 0x10, bpp: match data.color_type {
745 ImageColorType::Bgr => 0,
746 ImageColorType::Bgra => 1,
747 ImageColorType::Rgb => {
748 convert_rgb_to_bgr(&mut data)?;
749 0
750 }
751 ImageColorType::Rgba => {
752 convert_rgba_to_bgra(&mut data)?;
753 1
754 }
755 _ => {
756 return Err(anyhow::anyhow!(
757 "Unsupported color type: {:?}",
758 data.color_type
759 ));
760 }
761 },
762 mode: 0,
763 clips: Vec::new(),
764 };
765 let pixel_size = data.color_type.bpp(1) as u8;
766 if data.color_type == ImageColorType::Bgra && header.mode != 1 {
767 let alpha_flip = if header.mode == 2 { 0 } else { 0xFF };
768 for i in (0..data.data.len()).step_by(4) {
769 let b = data.data[i];
770 let g = data.data[i + 1];
771 let r = data.data[i + 2];
772 let a = data.data[i + 3];
773 data.data[i] = a ^ alpha_flip;
774 data.data[i + 1] = b;
775 data.data[i + 2] = g;
776 data.data[i + 3] = r;
777 }
778 }
779 let mode = config.circus_crx_mode.for_creating();
780 let encoded = if mode.is_best() {
781 Self::encode_image_best(&data.data, header.width, header.height, pixel_size)?
782 } else if let CircusCrxMode::Fixed(mode) = mode {
783 Self::encode_image_fixed(&data.data, header.width, header.height, pixel_size, mode)?
784 } else {
785 return Err(anyhow::anyhow!(
786 "Unsupported row type for creating: {:?}",
787 mode
788 ));
789 };
790 let compressed = if config.circus_crx_zstd {
791 let mut encoder = zstd::Encoder::new(MemWriter::new(), config.zstd_compression_level)?;
792 encoder.write_all(&encoded)?;
793 let compressed_data = encoder.finish()?;
794 compressed_data.into_inner()
795 } else {
796 let mut encoder = flate2::write::ZlibEncoder::new(
797 MemWriter::new(),
798 flate2::Compression::new(config.zlib_compression_level),
799 );
800 encoder.write_all(&encoded)?;
801 let compressed_data = encoder.finish()?;
802 compressed_data.into_inner()
803 };
804 writer.write_all(b"CRXG")?;
805 header.pack(&mut writer, false, Encoding::Utf8)?;
806 writer.write_u32(compressed.len() as u32)?;
807 writer.write_all(&compressed)?;
808 Ok(())
809 }
810}
811
812impl Script for CrxImage {
813 fn default_output_script_type(&self) -> OutputScriptType {
814 OutputScriptType::Json
815 }
816
817 fn default_format_type(&self) -> FormatOptions {
818 FormatOptions::None
819 }
820
821 fn is_image(&self) -> bool {
822 true
823 }
824
825 fn export_image(&self) -> Result<ImageData> {
826 let data_size = self.color_type.bpp(1) as usize
827 * self.header.width as usize
828 * self.header.height as usize;
829 let mut data = vec![0; data_size];
830 let mut encode_type = Vec::new();
831 Self::decode_image(
832 &mut data,
833 &self.data,
834 self.header.width,
835 self.header.height,
836 self.color_type.bpp(1) as u8,
837 &mut encode_type,
838 )?;
839 if self.color_type.bpp(1) == 4 && self.header.mode != 1 {
840 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
841 for i in (0..data_size).step_by(4) {
842 let a = data[i];
843 let b = data[i + 1];
844 let g = data[i + 2];
845 let r = data[i + 3];
846 data[i] = b;
847 data[i + 1] = g;
848 data[i + 2] = r;
849 data[i + 3] = a ^ alpha_flip;
850 }
851 }
852 let img = ImageData {
853 width: self.header.width as u32,
854 height: self.header.height as u32,
855 depth: 8,
856 color_type: self.color_type,
857 data,
858 };
859 if self.canvas {
860 let (img_width, img_height) = if self.header.clips.is_empty() {
861 (self.header.width as u32, self.header.height as u32)
862 } else {
863 let clip = &self.header.clips[0];
864 (clip.img_width as u32, clip.img_height as u32)
865 };
866 return Ok(draw_on_canvas(
867 img,
868 img_width,
869 img_height,
870 self.header.inner_x as u32,
871 self.header.inner_y as u32,
872 )?);
873 }
874 Ok(img)
875 }
876
877 fn import_image<'a>(
878 &'a self,
879 mut data: ImageData,
880 mut file: Box<dyn WriteSeek + 'a>,
881 ) -> Result<()> {
882 let mut color_type = match data.color_type {
883 ImageColorType::Bgr => ImageColorType::Bgr,
884 ImageColorType::Bgra => ImageColorType::Bgra,
885 ImageColorType::Rgb => {
886 convert_rgb_to_bgr(&mut data)?;
887 ImageColorType::Bgr
888 }
889 ImageColorType::Rgba => {
890 convert_rgba_to_bgra(&mut data)?;
891 ImageColorType::Bgra
892 }
893 _ => {
894 return Err(anyhow::anyhow!(
895 "Unsupported color type: {:?}",
896 data.color_type
897 ));
898 }
899 };
900 if data.width != self.header.width as u32 {
901 return Err(anyhow::anyhow!(
902 "Image width does not match: expected {}, got {}",
903 self.header.width,
904 data.width
905 ));
906 }
907 if data.height != self.header.height as u32 {
908 return Err(anyhow::anyhow!(
909 "Image height does not match: expected {}, got {}",
910 self.header.height,
911 data.height
912 ));
913 }
914 if data.depth != 8 {
915 return Err(anyhow::anyhow!("Image depth must be 8, got {}", data.depth));
916 }
917 if data.color_type != self.color_type && self.keep_original_bpp {
918 if self.color_type == ImageColorType::Bgr {
919 convert_bgra_to_bgr(&mut data)?;
920 } else if self.color_type == ImageColorType::Bgra {
921 convert_bgr_to_bgra(&mut data)?;
922 } else {
923 return Err(anyhow::anyhow!(
924 "Unsupported color type for import: {:?}",
925 self.color_type
926 ));
927 }
928 color_type = self.color_type;
929 }
930 let mut new_header = self.header.clone();
931 new_header.bpp = match color_type {
932 ImageColorType::Bgr => 0,
933 ImageColorType::Bgra => 1,
934 _ => return Err(anyhow::anyhow!("Unsupported color type: {:?}", color_type)),
935 };
936 new_header.flags |= 0x10; let pixel_size = color_type.bpp(1) as u8;
938 if color_type == ImageColorType::Bgra && self.header.mode != 1 {
939 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
940 for i in (0..data.data.len()).step_by(4) {
941 let b = data.data[i];
942 let g = data.data[i + 1];
943 let r = data.data[i + 2];
944 let a = data.data[i + 3];
945 data.data[i] = a ^ alpha_flip;
946 data.data[i + 1] = b;
947 data.data[i + 2] = g;
948 data.data[i + 3] = r;
949 }
950 }
951 let encoded = if self.row_type.is_origin() {
952 let mut row_type = Vec::with_capacity(self.header.height as usize);
953 let row_len = self.header.width as usize * self.color_type.bpp(1) as usize + 1;
954 let mut cur_pos = 0;
955 for _ in 0..self.header.height {
956 row_type.push(self.data[cur_pos]);
957 cur_pos += row_len;
958 }
959 Self::encode_image_origin(
960 &data.data,
961 new_header.width,
962 new_header.height,
963 pixel_size,
964 &row_type,
965 )?
966 } else if self.row_type.is_best() {
967 Self::encode_image_best(&data.data, new_header.width, new_header.height, pixel_size)?
968 } else if let CircusCrxMode::Fixed(mode) = self.row_type {
969 Self::encode_image_fixed(
970 &data.data,
971 new_header.width,
972 new_header.height,
973 pixel_size,
974 mode,
975 )?
976 } else {
977 return Err(anyhow::anyhow!(
978 "Unsupported row type for import: {:?}",
979 self.row_type
980 ));
981 };
982 let compressed = if self.zstd {
983 let mut encoder = zstd::Encoder::new(MemWriter::new(), self.zstd_compression_level)?;
984 encoder.write_all(&encoded)?;
985 let compressed_data = encoder.finish()?;
986 compressed_data.into_inner()
987 } else {
988 let mut encoder = flate2::write::ZlibEncoder::new(
989 MemWriter::new(),
990 flate2::Compression::new(self.compress_level),
991 );
992 encoder.write_all(&encoded)?;
993 let compressed_data = encoder.finish()?;
994 compressed_data.into_inner()
995 };
996 file.write_all(b"CRXG")?;
997 new_header.pack(&mut file, false, Encoding::Utf8)?;
998 file.write_u32(compressed.len() as u32)?;
999 file.write_all(&compressed)?;
1000 Ok(())
1001 }
1002}
1003
1004fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
1005 if base.color_type != diff.color_type {
1006 return Err(anyhow::anyhow!(
1007 "Color types do not match: {:?} vs {:?}",
1008 base.color_type,
1009 diff.color_type
1010 ));
1011 }
1012 let bpp = base.color_type.bpp(1) as usize;
1013 let base_stride = base.width as usize * bpp;
1014 let diff_stride = diff.width as usize * bpp;
1015
1016 for y in 0..diff.height {
1017 let base_y = top + y;
1018 if base_y >= base.height {
1019 continue; }
1021
1022 for x in 0..diff.width {
1023 let base_x = left + x;
1024 if base_x >= base.width {
1025 continue; }
1027
1028 let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
1029 let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
1030
1031 let diff_pixel = &diff.data[diff_index..diff_index + bpp];
1032 let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
1033 let mut b = base_pixel_orig[0];
1034 let mut g = base_pixel_orig[1];
1035 let mut r = base_pixel_orig[2];
1036 wrapping! {
1037 b += diff_pixel[0];
1038 g += diff_pixel[1];
1039 r += diff_pixel[2];
1040 }
1041 base.data[base_index] = b;
1042 base.data[base_index + 1] = g;
1043 base.data[base_index + 2] = r;
1044 if bpp == 4 {
1045 let mut a = base_pixel_orig[3];
1046 wrapping! {
1047 a -= diff_pixel[3];
1048 }
1049 base.data[base_index + 3] = a;
1050 }
1051 }
1052 }
1053 Ok(())
1054}